/**
 * [10] Regular Expression Matching
 *
 * Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.
 *
 *
 * '.' Matches any single character.
 * '*' Matches zero or more of the preceding element.
 *
 *
 * The matching should cover the entire input string (not partial).
 *
 * Note:
 *
 *
 * 	s could be empty and contains only lowercase letters a-z.
 * 	p could be empty and contains only lowercase letters a-z, and characters like . or *.
 *
 *
 * Example 1:
 *
 *
 * Input:
 * s = "aa"
 * p = "a"
 * Output: false
 * Explanation: "a" does not match the entire string "aa".
 *
 *
 * Example 2:
 *
 *
 * Input:
 * s = "aa"
 * p = "a*"
 * Output: true
 * Explanation: '*' means zero or more of the precedeng element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
 *
 *
 * Example 3:
 *
 *
 * Input:
 * s = "ab"
 * p = ".*"
 * Output: true
 * Explanation: ".*" means "zero or more (*) of any character (.)".
 *
 *
 * Example 4:
 *
 *
 * Input:
 * s = "aab"
 * p = "c*a*b"
 * Output: true
 * Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches "aab".
 *
 *
 * Example 5:
 *
 *
 * Input:
 * s = "mississippi"
 * p = "mis*is*p*."
 * Output: false
 *
 *
 */
pub struct Solution {}

// problem: https://leetcode.com/problems/regular-expression-matching/
// discuss: https://leetcode.com/problems/regular-expression-matching/discuss/?currentPage=1&orderBy=most_votes&query=

// submission codes start here
impl Solution {
    pub fn is_match(s: String, p: String) -> bool {
        let s: Vec<char> = s.chars().collect();
        let p: Vec<char> = p.chars().collect();
        let m = s.len() + 1;
        let n = p.len() + 1;
        let char_matched = |i: usize, j: usize| -> bool { s[i] == p[j] || p[j] == '.' };
        let mut f = vec![vec![false; n]; m];
        f[0][0] = true;
        for j in 1..n {
            f[0][j] = p[j - 1] == '*' && f[0][j - 2];
        }
        for i in 1..m {
            for j in 1..n {
                if char_matched(i - 1, j - 1) {
                    f[i][j] = f[i - 1][j - 1];
                    continue;
                }
                if p[j - 1] == '*' {
                    f[i][j] = f[i][j - 2] || (char_matched(i - 1, j - 2) && f[i - 1][j]);
                }
            }
        }
        f[m - 1][n - 1]
    }
}
// submission codes end

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_1() {
        let s = String::from("aa");
        let p = String::from("a");
        assert_eq!(false, Solution::is_match(s, p));
    }
    #[test]
    fn test_2() {
        let s = String::from("aa");
        let p = String::from("a*");
        assert_eq!(true, Solution::is_match(s, p));
    }
    #[test]
    fn test_3() {
        let s = String::from("ab");
        let p = String::from(".*");
        assert_eq!(true, Solution::is_match(s, p));
    }
    #[test]
    fn test_4() {
        let s = String::from("aab");
        let p = String::from("c*a*b");
        assert_eq!(true, Solution::is_match(s, p));
    }
    #[test]
    fn test_5() {
        let s = String::from("mississippi");
        let p = String::from("mis*is*p*.");
        assert_eq!(false, Solution::is_match(s, p));
    }
}
